package com.nlp.visualization.core.discourse.impl;

import com.google.gson.Gson;
import com.hankcs.hanlp.classification.classifiers.IClassifier;
import com.hankcs.hanlp.classification.classifiers.NaiveBayesClassifier;
import com.hankcs.hanlp.classification.models.AbstractModel;
import com.hankcs.hanlp.classification.models.NaiveBayesModel;
import com.nlp.visualization.core.discourse.IBayesService;
import com.nlp.visualization.pojo.bayes.BayesModel;
import com.nlp.visualization.pojo.db.TModel;
import com.nlp.visualization.service.backEnd.IModelsService;
import com.nlp.visualization.utils.MD5Util;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author LXM
 * @Title: NLPVisualizationTools
 * @Description:
 * @date 2018/5/9下午2:28
 */
@Service
public class BayesServiceImpl implements IBayesService {

    @Autowired
    private IModelsService modelsService;

    //存放训练材料的基路径
    private static final String CLASSIFIER_BASE_PATH = "/Users/lxm/MyComputer/Projects/NLPVisualizationWebApp/src/main/resources/data/bayes/data/";
    //存放训练好的模型的基路径
    private static final String CLASSIFIER_MODEL_BASE_PATH = "/Users/lxm/MyComputer/Projects/NLPVisualizationWebApp/src/main/resources/data/bayes/model/";

    Logger logger = LoggerFactory.getLogger(BayesServiceImpl.class);

    Gson gson = new Gson();

    /**
     * 对指定目录下的文档做贝叶斯分类器的训练
     *
     * @param name
     * @param filePath
     */
    @Override

    public String trainClassifier(String name, String filePath) {

        String modelName;
        // 创建分类器，更高级的功能请参考IClassifier的接口定义
        IClassifier classifier = new NaiveBayesClassifier();
        // 训练后的模型支持持久化，下次就不必训练了
        try {
            classifier.train(CLASSIFIER_BASE_PATH + filePath);
            AbstractModel model = classifier.getModel();
            modelName = saveModel(name, model);
            return modelName;
        } catch (IOException e) {
            logger.error("训练贝叶斯模型异常", e);
            return "";
        }

    }


    /**
     * 对用户指定的文本做贝叶斯分类器的训练，返回分类器的持久化模型的MD5码
     *
     * @param name
     * @param map
     */
    @Override
    public String trainClassifier(String name, Map<String, String[]> map) {
        String modelName;
        // 创建分类器，更高级的功能请参考IClassifier的接口定义
        IClassifier classifier = new NaiveBayesClassifier();
        // 训练后的模型支持持久化，下次就不必训练了
        try {
            classifier.train(map);
            AbstractModel model = classifier.getModel();
            modelName = saveModel(name, model);
            //通过数据库将模型信息存储起来
            String description = "";
            TModel tModel = new TModel();
            tModel.setName(name);
            Set<Map.Entry<String, String[]>> entries = map.entrySet();
            for (Map.Entry<String, String[]> entry : entries) {
                description += entry.getKey() + "|";
            }
            tModel.setDir(CLASSIFIER_MODEL_BASE_PATH + "/" + modelName + ".model");
            tModel.setDescription("【模型文件】-【贝叶斯模型】-" + description);
            modelsService.save(tModel);
            return modelName;
        } catch (Exception e) {
            logger.error("训练贝叶斯模型异常", e);
            return "";
        }

    }

    /**
     * 对指定的分类器，对文本text做预测
     *
     * @param classifier
     * @param text
     * @return
     */
    @Override
    public Map predict(String modelTag, List<String> texts) {

        //返回结果的Map
        Map resMap = new HashedMap();
        //存放数据的List
        List list = new ArrayList();
        //根据标签获取一个分类器的训练模型
        NaiveBayesModel model = getModel(modelTag);
        // 创建分类器，更高级的功能请参考IClassifier的接口定义
        IClassifier classifier = new NaiveBayesClassifier(model);

        logger.info(model.catalog[0]);
        for (String text : texts) {
            Map map = new HashedMap();
            List c_list = new ArrayList();
            Map classifyMap = classifier.predict(text);
            map.put("text", text);
            for (String s : model.catalog) {
                Map tmpMap = new HashedMap(2);
                tmpMap.put("name", s);
                tmpMap.put("value", classifyMap.get(s));
                c_list.add(tmpMap);
            }
            map.put("classify", c_list);
            list.add(map);
        }
        resMap.put("res", list);
        resMap.put("catlog", model.catalog);
        return resMap;

    }


    /**
     * 序列化后保存模型
     *
     * @param name
     * @param model
     */
    private String saveModel(String name, AbstractModel model) {
        if (model == null)
            return null;
        String modelString = gson.toJson(model);
        String md5 = MD5Util.MD5(System.currentTimeMillis() + "");
        String fileName = md5 + ".model";
        String fullPath = CLASSIFIER_MODEL_BASE_PATH + fileName;
        File file = new File(fullPath);
//        try {
//            FileUtils.writeStringToFile(file, modelString, "UTF-8");
//        } catch (IOException e) {
//            logger.error("保存模型异常", e);
//        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            //对象序列化过程
            FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(model);
            oos.flush();
            oos.close();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5;
    }

    /**
     * 根据MD5取出文件并且返回模型
     *
     * @param md5
     * @return
     */
    private NaiveBayesModel getModel(String md5) {

        NaiveBayesModel model = new NaiveBayesModel();

        File file = new File(CLASSIFIER_MODEL_BASE_PATH + md5 + ".model");
        if (!file.exists())
            return null;
        try {
//            String modelString = FileUtils.readFileToString(file);
//            BayesModel bayesModel = gson.fromJson(modelString, BayesModel.class);
//            model.n = bayesModel.n;
//            model.c = bayesModel.c;
//            model.d = bayesModel.d;
//            model.logLikelihoods = bayesModel.logLikelihoods;
//            model.logPriors = bayesModel.logPriors;
//            model.catalog = bayesModel.catalog;
//            model.tokenizer = bayesModel.tokenizer;
//            model.wordIdTrie = bayesModel.wordIdTrie;
            //Student对象反序列化过程
            FileInputStream fis = new FileInputStream(file);
            ObjectInputStream ois = new ObjectInputStream(fis);
            model = (NaiveBayesModel) ois.readObject();
            ois.close();
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        return model;

    }


    public static void main(String[] args) {

        BayesServiceImpl bayesService = new BayesServiceImpl();
        //1、用于预测部分
//       bayesService.trainClassifier("emotion", "comment");
//        List list = new ArrayList();
//        list.add("真垃圾，太吵了");
//        bayesService.predict("3AA0496FD96F3EBD27EF493C0D242469", list);

        //2、用于训练情感
//        List<String> neg = new ArrayList<>();
//        List<String> pos = new ArrayList<>();
//        Map map = new HashedMap();
//        File file = new File("/Users/lxm/Downloads/BosonNLP_sentiment_score 2/BosonNLP_sentiment_score.txt");
//        try {
//            List<String> strings = FileUtils.readLines(file);
//            for (String s : strings) {
//                String each[] = s.split("\\s+");
//                if (each.length < 2) continue;
//                if (each[1].indexOf("-") < 0) {
//                    pos.add(each[0]);
//                } else {
//                    neg.add(each[0]);
//                }
//            }
//            String[] negArr = neg.toArray(new String[neg.size()]);
//            String[] posArr = pos.toArray(new String[pos.size()]);
//            map.put("正面", posArr);
//            map.put("负面", negArr);
//            String md5 = bayesService.trainClassifier("weibo", map);
//            System.out.println(md5);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }


        List<String> neg = new ArrayList<>();
        List<String> pos = new ArrayList<>();
        Map map = new HashedMap();
        File neg_file = new File("/Users/lxm/MyComputer/Projects/NLPVisualizationWebApp/src/main/resources/data/bayes/data/movie_(english)/负面/neg_movie_reviews.txt");
        File pos_file = new File("/Users/lxm/MyComputer/Projects/NLPVisualizationWebApp/src/main/resources/data/bayes/data/movie_(english)/正面/pos_movie_reviews.txt");
        try {
            List<String> strings = FileUtils.readLines(pos_file);
            for (String s : strings) {
                pos.add(s.trim());
            }
            List<String> negStr = FileUtils.readLines(neg_file);
            for (String s : negStr) {
                neg.add(s.trim());
            }
            String[] negArr = neg.toArray(new String[neg.size()]);
            String[] posArr = pos.toArray(new String[pos.size()]);
            map.put("正面", posArr);
            map.put("负面", negArr);
            String md5 = bayesService.trainClassifier("cnki", map);
            System.out.println(md5);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}
